package com.wande.dataplatform.filecollection.parser.impl;

import cn.hutool.core.util.StrUtil;
import com.wande.dataplatform.filecollection.common.enums.FileCollectionErrorCode;
import com.wande.dataplatform.filecollection.common.exception.FileCollectionException;
import com.wande.dataplatform.filecollection.domain.dto.FieldInfo;
import com.wande.dataplatform.filecollection.parser.AbstractFileParser;
import com.wande.dataplatform.filecollection.parser.ParserConfig;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.*;
import java.util.function.Consumer;

/**
 * XML文件解析器
 * 支持XPath表达式定位数据节点
 * 支持命名空间处理
 * 支持流式解析
 *
 * @author dataplatform
 * @date 2025-01-24
 */
@Slf4j
@Component
public class XmlFileParser extends AbstractFileParser {

    private static final int SAMPLE_SIZE = 100;
    private static final String DEFAULT_DATA_XPATH = "//record";

    @Override
    public String getSupportedFileType() {
        return "XML";
    }

    @Override
    public List<FieldInfo> detectFields(InputStream inputStream, ParserConfig config) {
        validateInputStream(inputStream);
        validateConfig(config);

        List<FieldInfo> fields = new ArrayList<>();

        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);

            String dataXPath = getDataXPath(config);
            List<Node> nodes = document.selectNodes(dataXPath);

            if (nodes.isEmpty()) {
                return fields;
            }

            // 收集所有字段名和样本数据
            Map<String, List<String>> fieldSamples = new LinkedHashMap<>();
            int sampleCount = 0;

            for (Node node : nodes) {
                if (sampleCount >= SAMPLE_SIZE) {
                    break;
                }

                if (node instanceof Element) {
                    Element element = (Element) node;
                    extractFieldsFromElement(element, "", fieldSamples);
                }
                sampleCount++;
            }

            // 创建字段信息
            int index = 0;
            for (Map.Entry<String, List<String>> entry : fieldSamples.entrySet()) {
                String fieldName = cleanFieldName(entry.getKey());
                List<String> samples = entry.getValue();
                String fieldType = inferFieldTypeFromSamples(samples);

                FieldInfo fieldInfo = createFieldInfo(fieldName, fieldType, index);
                if (!samples.isEmpty()) {
                    fieldInfo.setSampleValue(samples.get(0));
                }
                fields.add(fieldInfo);
                index++;
            }

        } catch (DocumentException e) {
            log.error("Failed to detect XML fields", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_PARSE_ERROR, "Failed to detect XML fields: " + e.getMessage());
        }

        return fields;
    }

    @Override
    public Iterator<Map<String, Object>> parse(InputStream inputStream, ParserConfig config) {
        validateInputStream(inputStream);
        validateConfig(config);

        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);

            String dataXPath = getDataXPath(config);
            List<Node> nodes = document.selectNodes(dataXPath);

            return new XmlIterator(nodes, config);

        } catch (DocumentException e) {
            log.error("Failed to parse XML file", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_PARSE_ERROR, "Failed to parse XML file: " + e.getMessage());
        }
    }

    @Override
    public void parseStream(InputStream inputStream, ParserConfig config, Consumer<Map<String, Object>> consumer) {
        validateInputStream(inputStream);
        validateConfig(config);

        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);

            String dataXPath = getDataXPath(config);
            List<Node> nodes = document.selectNodes(dataXPath);

            for (Node node : nodes) {
                if (node instanceof Element) {
                    Element element = (Element) node;
                    Map<String, Object> rowData = extractDataFromElement(element, "", config);

                    // 跳过空行
                    if (config.getSkipEmptyRows() && isEmptyRow(rowData)) {
                        continue;
                    }

                    consumer.accept(rowData);
                }
            }

        } catch (DocumentException e) {
            log.error("Failed to parse XML file in stream mode", e);
            throw new FileCollectionException(FileCollectionErrorCode.FILE_PARSE_ERROR, "Failed to parse XML file in stream mode: " + e.getMessage());
        }
    }

    @Override
    public boolean validate(InputStream inputStream, ParserConfig config) {
        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);
            return document.getRootElement() != null;
        } catch (Exception e) {
            log.warn("XML validation failed: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取数据节点XPath
     *
     * @param config 解析器配置
     * @return XPath表达式
     */
    private String getDataXPath(ParserConfig config) {
        if (StrUtil.isNotBlank(config.getDataXPath())) {
            return config.getDataXPath();
        }
        if (StrUtil.isNotBlank(config.getRootXPath())) {
            return config.getRootXPath();
        }
        return DEFAULT_DATA_XPATH;
    }

    /**
     * 从元素中提取字段（用于字段检测）
     *
     * @param element 元素
     * @param prefix 字段名前缀
     * @param fieldSamples 字段样本数据
     */
    private void extractFieldsFromElement(Element element, String prefix, Map<String, List<String>> fieldSamples) {
        // 处理元素属性
        for (Object attrObj : element.attributes()) {
            org.dom4j.Attribute attr = (org.dom4j.Attribute) attrObj;
            String fieldName = prefix + "@" + attr.getName();
            fieldSamples.computeIfAbsent(fieldName, k -> new ArrayList<>()).add(attr.getValue());
        }

        // 处理子元素
        List<Element> children = element.elements();
        if (children.isEmpty()) {
            // 叶子节点，直接取文本值
            String fieldName = StrUtil.isBlank(prefix) ? element.getName() : prefix + "_" + element.getName();
            String value = element.getTextTrim();
            fieldSamples.computeIfAbsent(fieldName, k -> new ArrayList<>()).add(value);
        } else {
            // 非叶子节点，递归处理
            for (Element child : children) {
                String childPrefix = StrUtil.isBlank(prefix) ? child.getName() : prefix + "_" + child.getName();
                extractFieldsFromElement(child, childPrefix, fieldSamples);
            }
        }
    }

    /**
     * 从元素中提取数据
     *
     * @param element 元素
     * @param prefix 字段名前缀
     * @param config 解析器配置
     * @return 数据映射
     */
    private Map<String, Object> extractDataFromElement(Element element, String prefix, ParserConfig config) {
        Map<String, Object> data = new LinkedHashMap<>();

        // 处理元素属性
        for (Object attrObj : element.attributes()) {
            org.dom4j.Attribute attr = (org.dom4j.Attribute) attrObj;
            String fieldName = cleanFieldName(prefix + "@" + attr.getName());
            String value = processFieldValue(attr.getValue(), config);
            data.put(fieldName, value);
        }

        // 处理子元素
        List<Element> children = element.elements();
        if (children.isEmpty()) {
            // 叶子节点，直接取文本值
            String fieldName = cleanFieldName(StrUtil.isBlank(prefix) ? element.getName() : prefix + "_" + element.getName());
            String value = processFieldValue(element.getTextTrim(), config);
            data.put(fieldName, value);
        } else {
            // 非叶子节点，递归处理
            for (Element child : children) {
                String childPrefix = StrUtil.isBlank(prefix) ? child.getName() : prefix + "_" + child.getName();
                Map<String, Object> childData = extractDataFromElement(child, childPrefix, config);
                data.putAll(childData);
            }
        }

        return data;
    }

    /**
     * XML迭代器
     */
    private class XmlIterator implements Iterator<Map<String, Object>> {
        private final List<Node> nodes;
        private final ParserConfig config;
        private int currentIndex;

        public XmlIterator(List<Node> nodes, ParserConfig config) {
            this.nodes = nodes;
            this.config = config;
            this.currentIndex = 0;
        }

        @Override
        public boolean hasNext() {
            return currentIndex < nodes.size();
        }

        @Override
        public Map<String, Object> next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }

            Node node = nodes.get(currentIndex);
            currentIndex++;

            if (node instanceof Element) {
                Element element = (Element) node;
                return extractDataFromElement(element, "", config);
            }

            return new LinkedHashMap<>();
        }
    }
}
